use super::FudaThing;
use anyhow::Result;
use derive_more::Display;
use serde::{Deserialize, Serialize};
use surrealdb::sql::Thing;
use ts_rs::TS;
use yata::core::IndicatorResult;

pub mod adx;
pub mod aroon;
pub mod aso;
pub mod boll;
pub mod cci;
pub mod cko;
pub mod cks;
pub mod cmf;
pub mod cmo;
pub mod cpc;
pub mod dcc;
pub mod dpo;
pub mod efi;
pub mod eom;
pub mod evl;
pub mod ftf;
pub mod hma;
pub mod idc_score;
pub mod ikc;
pub mod kama;
pub mod kdj;
pub mod knc;
pub mod kst;
pub mod kvo;
pub mod macd;
pub mod mfi;
pub mod mti;
pub mod pcs;
pub mod prs;
pub mod rsi;
pub mod rvi;
pub mod sar;
pub mod sei;
pub mod tdsi;
pub mod trix;
pub mod trsi;
pub mod wcci;

pub mod trend_line;

// An enum to represent trading signals.
#[derive(Debug, Serialize, Deserialize, PartialEq, Display, TS, Clone, Copy)]
#[serde(rename_all = "UPPERCASE")]
#[ts(export)]
pub enum Signal {
    Buy,
    Sell,
    None,
}
/// An enum to specify the source of price data, assuming it's part of the library's design.
#[derive(Debug, Serialize, Deserialize, PartialEq, Display, TS, Clone, Copy)]
#[serde(rename_all = "UPPERCASE")]
#[ts(export)]
pub enum SourceType {
    Open,
    High,
    Low,
    Close,
    // Potentially other types like 'Median', 'TypicalPrice', etc., depending on library capabilities.
}

/// Every `Indicator` proceed an input of [`OHLCV`](crate::core::OHLCV) and returns an `IndicatorResult`
/// which consist of some returned raw values and some calculated signals.
///
/// `Indicator` may return up to 4 signals and 4 raw values at each step
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct IndicatorResultWrap {
    /// signnal 0
    pub s0: Option<IndicatorActionWrap>,
    /// signnal 1
    pub s1: Option<IndicatorActionWrap>,
    /// signnal 2
    pub s2: Option<IndicatorActionWrap>,
    /// signnal 3
    pub s3: Option<IndicatorActionWrap>,

    /// value 0
    pub v0: f64,
    /// value 1
    pub v1: f64,
    /// value 2
    pub v2: f64,
    /// value 3
    pub v3: f64,

    /// `Indicator` may return up to 4 signals and 4 raw values at each step
    pub slen: u8,
    /// `Indicator` may return up to 4 signals and 4 raw values at each step
    pub vlen: u8,
}

#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct IndicatorActionWrap {
    /// Buy signal's value, only >0 will be useful
    pub buy: i64,

    /// Sell signal's value, only >0 will be useful
    pub sell: i64,
}

impl From<&IndicatorResult> for IndicatorResultWrap {
    fn from(ir: &IndicatorResult) -> Self {
        let slen = ir.signals_length();
        let vlen = ir.values_length();

        IndicatorResultWrap {
            s0: signal_to_action_wrap(ir, 0, slen as usize),
            s1: signal_to_action_wrap(ir, 1, slen as usize),
            s2: signal_to_action_wrap(ir, 2, slen as usize),
            s3: signal_to_action_wrap(ir, 3, slen as usize),

            v0: value_or_default(ir, 0, vlen as usize),
            v1: value_or_default(ir, 1, vlen as usize),
            v2: value_or_default(ir, 2, vlen as usize),
            v3: value_or_default(ir, 3, vlen as usize),

            slen,
            vlen,
        }
    }
}

fn signal_to_action_wrap(
    ir: &IndicatorResult,
    index: usize,
    signals_length: usize,
) -> Option<IndicatorActionWrap> {
    if signals_length > index {
        match ir.signal(index) {
            yata::core::Action::Buy(v) => {
                if v > 0 {
                    Some(IndicatorActionWrap {
                        buy: v as i64,
                        sell: 0,
                    })
                } else {
                    None
                }
            }
            yata::core::Action::None => None,
            yata::core::Action::Sell(v) => {
                if v > 0 {
                    Some(IndicatorActionWrap {
                        buy: 0,
                        sell: v as i64,
                    })
                } else {
                    None
                }
            }
        }
    } else {
        None
    }
}

fn value_or_default(ir: &IndicatorResult, index: usize, values_length: usize) -> f64 {
    let v = if values_length > index {
        ir.value(index)
    } else {
        0.0
    };

    // fix f64::NAN and f64::INFINITY
    // test/test> select klt,tdsi from kline where tdsi.main > 1000 or tdsi.main < -1000
    // [[{ klt: 'DAY', tdsi: { main: NaN, signal0: 'NONE', signal1: 'NONE' } },
    //   { klt: 'M005', tdsi: { main: NaN, signal0: 'NONE', signal1: 'NONE' } },
    //   { klt: 'M015', tdsi: { main: NaN, signal0: 'NONE', signal1: 'NONE' } },
    //   { klt: 'M030', tdsi: { main: NaN, signal0: 'NONE', signal1: 'NONE' } },
    //   { klt: 'MONTH', tdsi: { main: NaN, signal0: 'NONE', signal1: 'NONE' } },
    //   { klt: 'WEEK', tdsi: { main: inf, signal0: 'NONE', signal1: 'NONE' } }]]
    if v.is_normal() {
        v
    } else {
        0.0
    }
}

#[derive(Display, Serialize, Deserialize, Debug, TS, PartialEq, Eq, Hash, Clone, Copy)]
#[ts(export)]
pub enum IndicatorGroupKey {
    ADX,
    AROON,
    ASO,
    BOLL,
    CCI,
    CKO,
    CKS,
    CMF,
    CMO,
    CPC,
    DCC,
    DPO,
    EFI,
    EOM,
    EVL,
    FTF,
    HMA,
    IKC,
    KAMA,
    KDJ,
    KNC,
    KST,
    KVO,
    MACD,
    MFI,
    MTI,
    PCS,
    PRS,
    RSI,
    RVI,
    SAR,
    SEI,
    TDSI,
    TRIX,
    TRSI,
}

impl Default for IndicatorGroupKey {
    fn default() -> Self {
        IndicatorGroupKey::ADX
    }
}

#[derive(Serialize, Deserialize, Debug, TS, Clone, Default)]
#[ts(export)]
pub struct Indicator {
    /// 唯一标识符，由 indicator+gk,e.g. indicator_adx
    #[ts(as = "Option<FudaThing>")]
    pub id: Option<Thing>,
    /// group key e.g. ADX, AROON, MACD
    pub gk: IndicatorGroupKey,
    /// name of the indicator
    pub name: Option<String>,
    /// desc of the indicator
    pub desc: Option<String>,
    /// prompt of the indicator for AI
    pub prompt: Option<String>,
}

impl Indicator {
    /**
     * 获取Inndicator记录的ID
     *
     * @param gk: IndicatorGroupKey
     *
     * @return: 记录的ID
     */
    pub fn id_from(gk: &IndicatorGroupKey) -> Result<String> {
        Ok(format!("indicator.{:?}", gk))
    }
}

// Using static with Mutex for potential interior mutability
lazy_static::lazy_static! {
    pub static ref INDICATOR_GROUP_KEYS : Vec<IndicatorGroupKey>= vec![
        IndicatorGroupKey::ADX,
        IndicatorGroupKey::AROON,
        IndicatorGroupKey::ASO,
        IndicatorGroupKey::BOLL,
        IndicatorGroupKey::CCI,
        IndicatorGroupKey::CKO,
        IndicatorGroupKey::CKS,
        IndicatorGroupKey::CMF,
        IndicatorGroupKey::CMO,
        IndicatorGroupKey::CPC,
        IndicatorGroupKey::DCC,
        IndicatorGroupKey::DPO,
        IndicatorGroupKey::EFI,
        IndicatorGroupKey::EOM,
        IndicatorGroupKey::EVL,
        IndicatorGroupKey::FTF,
        IndicatorGroupKey::HMA,
        IndicatorGroupKey::IKC,
        IndicatorGroupKey::KAMA,
        IndicatorGroupKey::KDJ,
        IndicatorGroupKey::KNC,
        IndicatorGroupKey::KST,
        IndicatorGroupKey::KVO,
        IndicatorGroupKey::MACD,
        IndicatorGroupKey::MFI,
        IndicatorGroupKey::MTI,
        IndicatorGroupKey::PCS,
        IndicatorGroupKey::RSI,
        IndicatorGroupKey::RVI,
        IndicatorGroupKey::SAR,
        IndicatorGroupKey::SEI,
        IndicatorGroupKey::TDSI,
        IndicatorGroupKey::TRIX,
        IndicatorGroupKey::TRSI,
    ];
}
